package com.xuecheng.manage_media.service;

import com.alibaba.fastjson.JSON;
import com.xuecheng.framework.domain.media.MediaFile;
import com.xuecheng.framework.domain.media.response.CheckChunkResult;
import com.xuecheng.framework.domain.media.response.MediaCode;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CommonCode;
import com.xuecheng.framework.model.response.ResponseResult;
import com.xuecheng.manage_media.config.RabbitMQConfig;
import com.xuecheng.manage_media.dao.MediaMongoDbDao;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

/**
 * @author ShengJinFeng
 * @version V1.0
 * @ClassName MediaUploadService
 * @Description (这里用一句话描述这个类的作用)
 * @date 2020/3/1 16:08
 * @since V1.0
 */
@Service
public class MediaUploadService {

    private final static Logger LOGGER = LoggerFactory.getLogger(MediaUploadService.class);

    @Value("${xc-service-manage-media.upload-location}")
    private String uploadLocation;

    @Value("${xc-service-manage-media.mq.routingkey-media-video}")
    private String routingkeyMediaVideo;

    @Autowired
    private MediaMongoDbDao mediaMongoDbDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * @Description 文件上传注册
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {

        //检查文件是否存在
        String filePath  = getFilePath(fileMd5,fileExt);
        File file = new File(filePath);
        boolean exists = file.exists();
        //检查数据库文件是否存在
        MediaFile mediaFile = mediaMongoDbDao.queryById(fileMd5);
        if (exists && mediaFile != null){//已存在
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }
        //文件不存在，创建文件夹
        Boolean fileFold = createFileFold(fileMd5);
        if (!fileFold){
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_CREATEFOLDER_FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * @Description 分块检查,查看分块是否存在
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    public CheckChunkResult checkchunk(String fileMd5, Integer chunk, Integer chunkSize) {
        //获取分块所在的文件夹路径
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        //获取分块文件
        File file = new File(chunkFileFolderPath+chunk);
        if (file.exists()){
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,true);
        }else{
            return new CheckChunkResult(MediaCode.CHUNK_FILE_EXIST_CHECK,false);
        }
    }

    /**
     * @Description 上传分块
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    public ResponseResult uploadchunk(MultipartFile file, Integer chunk, String fileMd5) {
        //检查保存分块的文件夹是否存在，不存在在创建
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkFileFolder = new File(chunkFileFolderPath);
        if (!chunkFileFolder.exists()){
            chunkFileFolder.mkdirs();
        }
        //获取保存分块的文件
        File chunkFile = new File(chunkFileFolderPath+chunk);
        //上传文件
        InputStream inputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            inputStream = file.getInputStream();
            fileOutputStream = new FileOutputStream(chunkFile);
            IOUtils.copy(inputStream,fileOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error("upload chunk file fail:{}",e.getMessage());
            ExceptionCast.cast(MediaCode.CHUNK_FILE_UPLOAD_FAIL);
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOutputStream.close();
            } catch (IOException e) {
            }
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * @Description 合并文件
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //获取所有的分块文件
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkFileFolder = new File(chunkFileFolderPath);
        File[] files = chunkFileFolder.listFiles();
        List<File> fileList = Arrays.asList(files);
        //合并文件
        String mergeFilePath = getFilePath(fileMd5, fileExt);
        File mergeFile = new File(mergeFilePath);
        mergeFile = mergeFiles(fileList,mergeFile);
        if (mergeFile == null){
            ExceptionCast.cast(MediaCode.MERGE_FILE_FAIL);
        }
        //校验md5
        boolean checkResult = checkFileMd5(mergeFile,fileMd5);
        if (!checkResult){
            ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
        }
        //将信息存入mongo
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        mediaFile.setFileName(fileMd5+"."+fileExt);
        mediaFile.setFileOriginalName(fileName);
        //文件路径保存相对路径
        mediaFile.setFilePath(fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/");
        mediaFile.setFileSize(fileSize);
        mediaFile.setUploadTime(new Date());
        mediaFile.setMimeType(mimetype);
        mediaFile.setFileType(fileExt);
        mediaFile.setFileStatus("301002");
        mediaMongoDbDao.save(mediaFile);

        //发送视频处理消息
        sendProcessVideoMsg(mediaFile.getFileId());

        //返回结果
        return new ResponseResult(CommonCode.SUCCESS);
    }

    /**
     * @Description 发送视频处理消息
     * @author ShengJinFeng
     * @date 2020/3/2
     */
    public void sendProcessVideoMsg(String fileId) {
        MediaFile mediaFile = mediaMongoDbDao.queryById(fileId);
        if (mediaFile == null){
            ExceptionCast.cast(CommonCode.INVALID_PARAM);
        }
        Map<String,String> map = new HashMap<>();
        map.put("mediaId",fileId);
        String msg = JSON.toJSONString(map);
        try {
            rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK,routingkeyMediaVideo,msg);
            LOGGER.info("send media process task msg:{}",msg);
        }catch (Exception e){
            e.printStackTrace();
            LOGGER.error("send media process task error,msg is:{},error:{}",msg,e.getMessage());
            ExceptionCast.cast(CommonCode.FAIL);
        }

    }

    /**
     * @Description 校验md5
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    private boolean checkFileMd5(File mergeFile, String fileMd5) {
        if (mergeFile == null || StringUtils.isEmpty(fileMd5)){
            return false;
        }
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(mergeFile);
            String md5 = DigestUtils.md5Hex(fis);
            if (fileMd5.equalsIgnoreCase(md5)){
                return true;
            }else{
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.error("checkFileMd5 error,file is:{},md5 is: {}",mergeFile.getAbsoluteFile(),fileMd5);
            return false;
        }finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @Description 合并文件
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    private File mergeFiles(List<File> fileList,File mergeFile) {
        try {
            //判断合并文件是否存在，如果已存在，则删除，如果不存在则创建
            if (mergeFile.exists()){
                mergeFile.delete();
            }
            mergeFile.createNewFile();
            //对文件进行排序
            Collections.sort(fileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    if (Integer.parseInt(o1.getName())>Integer.parseInt(o2.getName())){
                        return 1;
                    }
                    return -1;
                }
            });
            //遍历集合进行读写
            RandomAccessFile raf_writer = new RandomAccessFile(mergeFile,"rw");
            byte[] bytes = new byte[1024];
            for (File file : fileList) {
                //创建读对象
                RandomAccessFile raf = new RandomAccessFile(file,"r");
                int len = -1;
                while ((len = raf.read(bytes)) !=-1){
                    raf_writer.write(bytes,0,len);
                }
                raf.close();
            }
            raf_writer.close();
            return mergeFile;
        } catch (IOException e) {
            e.printStackTrace();
            LOGGER.error("merge file error:{}",e.getMessage());
            return null;
        }
    }

    /**
     * @Description 获取分块所在的文件夹路径
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    private String getChunkFileFolderPath(String fileMd5) {
        return uploadLocation+fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/chunk/";
    }

    /**
     * @Description 获取文件夹路径
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    private String getFilePath(String fileMd5,String fileExt) {
        return uploadLocation+fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/"+fileMd5+"."+fileExt;
    }

    /**
     * @Description 获取文件夹路径
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    private String getFileFolderPath(String fileMd5){
        return uploadLocation+fileMd5.substring(0,1)+"/"+fileMd5.substring(1,2)+"/"+fileMd5+"/";
    }
    
    /**
     * @Description 创建保存视频的文件夹
     * @author ShengJinFeng
     * @date 2020/3/1
     */
    private Boolean createFileFold(String fileMd5){
        String fileFolderPath = getFileFolderPath(fileMd5);
        File file = new File(fileFolderPath);
        if (file.exists()){
            return true;
        }
        return file.mkdirs();
    }
}
